package io.kiki.sba.registry.lifecycle.impl;

import io.kiki.sba.registry.exception.DisposeException;
import io.kiki.sba.registry.exception.InitializeException;
import io.kiki.sba.registry.exception.StartException;
import io.kiki.sba.registry.exception.StopException;
import io.kiki.sba.registry.lifecycle.Lifecycle;
import io.kiki.sba.registry.lifecycle.LifecycleController;
import io.kiki.sba.registry.lifecycle.LifecycleState;
import io.kiki.sba.registry.lifecycle.LifecycleStateAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public abstract class AbstractLifecycle implements Lifecycle, LifecycleStateAware {

    private final LifecycleState lifecycleState;
    private final LifecycleController lifecycleController;
    private final Logger logger = LoggerFactory.getLogger(AbstractLifecycle.class);

    public AbstractLifecycle() {
        this.lifecycleController = new DefaultLifecycleController();
        this.lifecycleState = new DefaultLifecycleState(this, lifecycleController);
    }

    public AbstractLifecycle(LifecycleState lifecycleState, LifecycleController lifecycleController) {
        this.lifecycleState = lifecycleState;
        this.lifecycleController = lifecycleController;
    }

    @Override
    public void initialize() throws InitializeException {
        LifecycleState.LifecyclePhase phaseName = lifecycleState.getPhase();
        if (!lifecycleController.canInitialize(phaseName)) {
            logger.error("[initialize][can not init]{}, {}", phaseName, this);
            throw new IllegalStateException("can not initialize:" + phaseName + "," + this);
        }

        try {
            lifecycleState.setPhase(LifecycleState.LifecyclePhase.INITIALIZING);
            doInitialize();
            lifecycleState.setPhase(LifecycleState.LifecyclePhase.INITIALIZED);
        } catch (Exception e) {
            lifecycleState.rollback(e);
            throw new InitializeException(e);
        }
    }

    protected void doInitialize() throws InitializeException {
    }

    @Override
    public void start() throws StartException {
        LifecycleState.LifecyclePhase phaseName = lifecycleState.getPhase();
        if (!lifecycleController.canStart(phaseName)) {
            logger.error("[initialize][can not start]{},{}", phaseName, this);
            throw new IllegalStateException("can not start:" + phaseName + ", " + this);
        }

        try {
            lifecycleState.setPhase(LifecycleState.LifecyclePhase.STARTING);
            doStart();
            lifecycleState.setPhase(LifecycleState.LifecyclePhase.STARTED);
        } catch (Exception e) {
            lifecycleState.rollback(e);
            throw new StartException(e);
        }
    }

    protected void doStart() throws StartException {
    }

    @Override
    public void stop() throws StopException {
        LifecycleState.LifecyclePhase phaseName = lifecycleState.getPhase();
        if (!lifecycleController.canStop(phaseName)) {
            logger.error("[initialize][can not stop]{}, {}", phaseName, this);
            throw new IllegalStateException("can not stop:" + phaseName + "," + this);
        }

        try {
            lifecycleState.setPhase(LifecycleState.LifecyclePhase.STOPPING);
            doStop();
            lifecycleState.setPhase(LifecycleState.LifecyclePhase.STOPPED);
        } catch (Exception e) {
            lifecycleState.rollback(e);
            throw new StopException(e);
        }
    }

    protected void doStop() throws StopException {
    }

    @Override
    public void dispose() throws DisposeException {
        LifecycleState.LifecyclePhase phaseName = lifecycleState.getPhase();
        if (!lifecycleController.canDispose(phaseName)) {
            logger.error("[initialize][can not stop]{}, {}", phaseName, this);
            throw new IllegalStateException("can not dispose:" + phaseName + "," + this);
        }
        try {
            lifecycleState.setPhase(LifecycleState.LifecyclePhase.DISPOSING);
            doDispose();
            lifecycleState.setPhase(LifecycleState.LifecyclePhase.DISPOSED);
        } catch (Exception e) {
            lifecycleState.rollback(e);
            throw new DisposeException(e);
        }
    }

    protected void doDispose() throws DisposeException {
    }

    @Override
    public LifecycleState getLifecycleState() {
        return this.lifecycleState;
    }
}
